package cn.jtfadmin.core.modules.quartz.service.impl;

import cn.jtfadmin.base.lang.config.ApplicationHolder;
import cn.jtfadmin.core.modules.quartz.constants.QuartzJobConstant;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;


/**
 * 从spring ApplicationContext 中获取任务类来执行
 *
 * @author akfh
 * @version 1.0
 * @since 1.0
 */
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class SpringJob implements Job {

    private Logger logger = LoggerFactory.getLogger(SpringJob.class);


    public static boolean validExecutorInfo(String param) {
        try {
            String[] paramArray = param.split(",|，");
            String beanName = paramArray[0];
            String methodName = paramArray[1];
            Object bean = ApplicationHolder.getApplicationContext().getBean(beanName);
            Method[] methods = ReflectionUtils.getDeclaredMethods(bean.getClass());
            Method method = handMethod(methods, methodName, paramArray);
            return true;
        } catch (Exception e) {
            return false;
        }

    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        try {
            JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
            String param = jobDataMap.getString(QuartzJobConstant.JOB_DATA_PARAM);
            logger.debug("定时任务参数:{}, 描述:{}", param, context.getJobDetail().getDescription());
            String[] paramArray = param.split(",|，");
            String beanName = paramArray[0];
            String methodName = paramArray[1];
            Object bean = ApplicationHolder.getApplicationContext().getBean(beanName);
            Method[] methods = ReflectionUtils.getDeclaredMethods(bean.getClass());
            Method method = handMethod(methods, methodName, paramArray);//ReflectionUtils.findMethod(bean.getClass(), methodName);
            ConversionService conversionService = ApplicationHolder.getApplicationContext().getBean(ConversionService.class);
            int parameterCount = method.getParameterCount();
            if (parameterCount > 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                Object[] parameters = new Object[parameterTypes.length];
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (parameterTypes[i] == JobExecutionContext.class) {
                        parameters[i] = context;
                        continue;
                    }
                    if (paramArray.length - 2 <= i || !StringUtils.hasText(paramArray[i + 2])) {
                        parameters[i] = null;
                        continue;
                    }
                    boolean b = conversionService.canConvert(String.class, parameterTypes[i]);
                    if (b) {
                        parameters[i] = conversionService.convert(paramArray[i + 2].trim(), parameterTypes[i]);
                    } else {
                        throw new RuntimeException("定时任务参数转换异常:" + beanName + ", " + methodName + "," + parameterTypes[i]);
                    }
                }
                method.invoke(bean, parameters);
            } else {
                method.invoke(bean);
            }

        } catch (Exception ex) {
            logger.warn("定时任务执行异常", ex);
            throw new JobExecutionException(ex);
        }
    }


    private static Method handMethod(Method[] methods, String methodName, String[] paramArray) {
        int paramLength = paramArray.length - 2;
        ConversionService conversionService = ApplicationHolder.getApplicationContext().getBean(ConversionService.class);
        List<Method> executMethods = new ArrayList<>();
        for (int j = 0; j < methods.length; j++) {
            Method method = methods[j];
            String name = method.getName();
            if (!name.equals(methodName)) {
                continue;
            }
            int parameterCount = method.getParameterCount();
            if (parameterCount > 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                int skipParamterNum = 0;
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (parameterTypes[i] == JobExecutionContext.class) {
                        skipParamterNum++;
                        continue;
                    }
                    if (!conversionService.canConvert(String.class, parameterTypes[i])) {
                        continue;
                    }
                }
                if (parameterTypes.length - skipParamterNum == paramLength) {
                    executMethods.add(method);
                }
            } else if (paramLength == 0) {
                executMethods.add(method);
            }

        }
        if (executMethods.size() == 1) {
            return executMethods.get(0);
        } else {
            throw new IllegalArgumentException("定时任务获取方法错误，获取方法数量为" + executMethods.size());
        }
    }
}
